สร้างหน้าเว็บเพจใหม่เพื่อส่งข้อมูลไปที่ Google Form กันดีกว่า (มี Reverse engineer API นิดนึง)
Table of Contents
คำเตือน #
เนื้อหาในบทความนี้อ้างอิงจาก Google Form ในวันที่ 12 ตุลาคม 2019 เท่านั้น ในอานาคตหลักการและวิธีในนี้อาจจะใช้ไม่ได้อีก
Google Form ไม่ดีตรงไหน ทำไมต้องปรับแต่ง? #
รู้ๆกันอยู่นะครับว่า Google Form นั้นมีข้อดีมากมายหลายอย่าง เพิ่มความสะดวกสบายให้กับชีวิตเราได้มากเลย ที่สำคัญคือมันฟรีด้วย แต่ข้อเสียคือเครื่องมีอในการปรับแต่งหน้าตาของ Form มีค่อนข้างจำกัด เช่น เปลี่ยนรูปที่ header หรือเปลี่ยนสีพื้นหลังได้ ใครอยากได้มากกว่านี้ทำไม่ได้แล้วครับ (เปล่ี่ยน font ภาษาไทยยังไม่ได้เล้ย)
บทความนี้ก็จะขอบอกเลยว่า ตอนนี้เราไม่สามารถปรับแต่งมากไปกว่าที่ Google เค้าให้มาครับ แต่เรายังสามารถสร้าง webpage ขึ้นมาใหม่แล้ว ยิง request ไปที่ api ของ Google Form ได้ครับ โดยเราจะสามารถหาวิธีการส่งข้อมูลพวกนั้นได้ Google Chrome และ Postman ครับผม
เริ่มกันเลย! #
สร้าง form #
อันนี้ผมว่าหลายคนรู้ว่าจะทำยังไงก็เข้าไปที่ Google Drive แล้วก็เลือกสร้าง Form ใช่ไหมครับ แต่เชื่อว่าหลายคนไม่รู้ว่าทำแบบนี้ได้ ลองพิมพ์ form.new
ที่ address bar ของ browser แล้ว enter ดูสิครับ

คราวนี้ก็สร้างฟอร์มตามใจเลยครับ วันนี้ผมจะสมมติว่าตัวเองเป็นเจ้าของสวนมังคุดออร์แกนิก ที่อยากเปิด pre-order ล่วงหน้า

เสร็จแล้วก็ลองกรอกข้อมูลพร้อมกับส่ง

ดูผลลัพธ์ก็จะได้แบบนี้ตามปกตินะครับ

ลอง reverse engineer กันแบบงูๆปลาๆกันครับ #
ก่อนอื่นกลับไปที่หน้า form ก่อนนะครับ ใครอยู่ที่หน้าสร้างก็ไปที่ “แสดงตัวอย่าง” ครับ

เปิด Developer Tools เลยครับผม (Google Chrome Menu > More Tools > Developer Tools)

เมื่อหน้าต่าง Developer Tools ปรากฏขึ้นมาแล้ว ให้ไปเปิด tab “Network” เอาไว้นะครับ เราจะรอดูสิ่งที่เกิดขึ้นระหว่างที่ส่งข้อมูลเข้า Google Form

ลองกรอกข้อมูลแล้วส่งไปใหม่อีกสักรอบครับผม

เราจะเห็นอะไรหลายๆอย่างโผล่ขึ้นมาใน Network ใช่ไหมครับ ใครที่งงว่ามันคืออะไร มันก็คือสิ่งที่ browser ทำการ request ไปหา server ปลายทางในการกด submit ข้อมูลเมื่อสักครู่นั่นแหละครับ การจะดูออกว่าอะไรคืออะไร อันนี้จำเป็นต้องทำความเข้าใจการทำงานของ web และ server ปลายทางอยู่บ้างนะครับ แต่ในบทความนี้จะขออธิบายสั้นๆเลยนะครับ ให้ลองมองหา POST
request ที่เกิดขึ้น แล้วก็ลองมองหาว่าตัวไหนที่มีข้อมูลที่เราพึ่งกรอกติดไปใน body ด้วย ในตัวอย่างของผมคือ formResponse
อันแรกนั่นเองครับ

เพื่อให้สะดวกในการล้วงแคะแกะเกา ผมจะเอาส่วนนี้ไปทำงานต่อบน application Postman โดยการคลิกขวาที่ตัว request แล้วเลือก Copy > Copy as cURL ที่ต้องทำแบบนี้ก็เพราะว่า Postman รองรับกับ Import ด้วย text ครับ Copy as cURL เนี่ยเหมาะที่สุดเพราะเป็นคำสั่งที่รวมทุกอย่างไว้ให้ครบเลย

เปิด Postman ขึ้นมาครับ ใครยังไม่มี ก็ไปโหลดมาติดตั้งได้เลยที่ https://getpostman.com นะครับ เปิดขึ้นมาแล้วไปที่ปุ่ม Import ด้านบนเลยครับ หลังจากนั้นก็เลือก Paste Raw Text วาง cURL ลงไปใน text area แล้วกด Import เลยครับผม

เราจะได้ Request ใหม่ขึ้นมาหนึ่งตัวที่มี Url, Header และ Body มาเรียบร้อย

ผมจะลองเปลี่ยนข้อมูลใน body ใหม่จากส่วนที่เราเข้าใจนะครับ สังเกตจากข้อมูลที่ติดมาด้วยเราน่าจะพอเดาออกว่าอะไรเป็นอะไร อะไรที่ไม่รู้ทิ้งไว้ก่อนก็ได้ครับ ลองแก้แล้วลองกด Send ดูครับผม

หลังจากส่งไปแล้วเราจะได้ Response กลับมาเป็น HTML Code ซึ่งถ้าอยากดูสะดวกๆก็ลองเปิดเป็น Preview ดูได้ครับ ถ้าใครจำได้นี่มันก็คือหน้าตอบกลับของ Google Form นี่เอง

ลองกลับไปดูที่ Form ก็พบว่าข้อมูลที่ส่งเมื่อกี้มันเข้ามาแล้ว

เพื่อความมั่นใจเราต้องหาว่าอะไรจำเป็นหรือไม่จำเป็นในการส่ง request ครั้งนี้ก่อน โดยผมจะเริ่มจากตัด body ที่เราไม่รู้จักออกโดยคลิกเอาเครื่องหมายถูกออกจาก body ที่เราไม่รู้จักและเดาว่าไม่น่าจะได้ใช้

เมื่อส่งแล้วคำตอบก็ยังคงเข้าไปตามปกติดี.. แต่มันยังเหลืออีกหลายจุดนะอย่าพึ่งดีใจ

ผมมาดูที่ Header เพราะมันมักจะมี Token หรืออะไรเยอะแยะไปหมด ผมเริ่มจากเอาออกให้หมดเลย

เอาให้แน่นอนต้องไปลบ Cookies ของ Postman ออกด้วยนะ

ตอนนี้ผมลบออกหมดแล้ว ไม่เหลืออะไรแล้วพร้อมสำหรับการทดสองยิงครั้งต่อไป

ลองเปลี่ยนข้อมูลแล้ว request ไปอีกรอบ

เรียบร้อย แปลว่ามันต้องการแค่ body ของเราเท่านั้นเองครับ

ถ้าจะลองสรุปออกมา API ที่ผมแกะออกมามันก็จะมีหน้าตาประมาณนี้
{
"URL": "https://docs.google.com/forms/d/e/1FAIpQLSeRSWxqaW7BvoF47cl_muvGTvw39sm7Qt6DNYL0CkxQNVR0Ww/formResponse",
"Method": "POST",
"Header": {
"Content-Type": "application/x-www-form-urlencoded"
},
"Body": {
"entry.1819278497": "ชื่อ-นามสกุล",
"entry.1633641327": "เบอร์โทรศัพท์",
"entry.828954501": "จำนวนที่จอง",
}
}
สร้าง Form ใหม่กันเถอะ #
เพื่อความรวดเร็วผมจะไม่ขอลงรายละเอียดในส่วนของการทำหน้า page นะครับ ต่างคนก็น่าจะมีสไตล์การเขียนที่ต่างกัน แต่ในบทความนี้ผมจะเลือกใช้ Vuetify และ Vue.js ในการสร้างหน้าฟอร์มขึ้นมาใหม่นะครับ ที่เลือก Vuetify เพราะว่ามี components พร้อมอยู่แล้ว ประกอบกับ Document ที่มีให้ก็เขียนไว้ดีมากเลย
https://vuetifyjs.com/en/getting-started/quick-start

ผมสร้าง form แบบลวกๆขึ้นมาด้วย Vue.js และยัดทุกอย่างไว้ที่ App.vue
หน้าเดียวเลยก็จะได้หน้าตาประมาณนี้
<template>
<v-app>
<v-content>
<v-row
class="ma-5"
justify="center"
>
<v-col
cols="12"
md="8"
lg="6"
>
<v-card :elevation="24">
<v-row class="pa-6">
<v-col
cols="12"
md="5"
lg="4"
>
<v-img
src="@/assets/mangosteen.jpg"
position="center center"
height="100%"
>
</v-img>
</v-col>
<v-col
cols="12"
md="7"
lg="8"
>
<div class="title">
มังคุดออร์แกนิก ออนไลน์
</div>
<v-form
ref="form"
action=""
v-model="valid"
lazy-validation
>
<v-text-field
v-model="name"
:counter="280"
:rules="nameRules"
label="ชื่อ-นามสกุล ผู้จอง"
required
></v-text-field>
<v-text-field
v-model="phone"
:rules="phoneRules"
label="เบอร์โทรศัพท์"
hint="ไม่ต้องเติม - ในเบอร์โทรศัพท์"
required
></v-text-field>
<v-select
v-model="selectedPackage"
:items="packages"
:rules="[v => !!v || 'กรุณาเลือกจำนวนที่ต้องการจอง']"
label="จำนวนที่ต้องการจอง"
required
></v-select>
<v-row
class="px-5"
justify="space-between"
>
<v-btn
color="error"
@click="reset"
:loading="loading"
>
<v-icon left>mdi-delete</v-icon>
ล้างข้อมูล
</v-btn>
<v-btn
color="success"
@click="submit"
:loading="loading"
>
<v-icon left>mdi-shopping</v-icon>
สั่งจองเลย
</v-btn>
</v-row>
</v-form>
</v-col>
</v-row>
</v-card>
</v-col>
<v-snackbar v-model="snackbar">
{{ alertMessage }}
<v-btn
color="pink"
text
@click="snackbar = false"
>
ปิด
</v-btn>
</v-snackbar>
</v-row>
</v-content>
</v-app>
</template>
<script>
import qs from 'querystring';
export default {
name: 'App',
data: () => ({
loading: false,
valid: true,
name: '',
snackbar: false,
alertMessage: '',
nameRules: [
v => !!v || 'กรุณากรอกชื่อผู้จอง',
v => (v && v.length <= 280) || 'ชื่อไม่ควรเกิน 280 ตัวอักษร',
],
phone: '',
phoneRules: [
v => !!v || 'กรุณาระบุเบอร์โทรศัพท์ของท่าน',
v => (/\d{9,10}/.test(v) && v.length <= 10) || 'เบอร์โทรศัพท์ไม่ถูกต้อง',
],
selectedPackage: null,
packages: ['5 กก.', '10 กก.', '15 กก.', '20 กก.'],
}),
methods: {
reset() {
this.$refs.form.reset();
},
async submit() {
if (this.$refs.form.validate()) {
const url = 'https://docs.google.com/forms/d/e/1FAIpQLSeRSWxqaW7BvoF47cl_muvGTvw39sm7Qt6DNYL0CkxQNVR0Ww/formResponse';
const body = {
'entry.1819278497': this.name,
'entry.1633641327': this.phone,
'entry.828954501': this.selectedPackage,
};
try {
this.loading = true;
await this.postData(url, body);
this.reset();
this.alertMessage = 'สั่งจองให้เรียบร้อยแล้ว';
} catch (error) {
this.alertMessage = error.message;
} finally {
this.loading = false;
this.snackbar = true;
}
}
},
postData(url, data) {
return fetch(url, {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: qs.stringify(data),
});
},
},
};
</script>
เพียงแค่กด “สั่งจอง” ข้อมูลก็จะถูกส่งไปที่ Google Form ได้เลยทันที

หลายคนเขียนส่ง request ไปที่ API แล้วจะเจอปัญหา CORS ผมก็จะแก้ปัญหาด้วยการเปลี่ยน mode
เป็น no-cors
ใน function fetch()
นะครับ
postData(url, data) {
return fetch(url, {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: qs.stringify(data),
});
}
ใครยังไม่เห็นภาพลองดูตัวอย่าง code และเว็บไซต์ใหม่ที่ผมทำขึ้นมาได้เลยตาม link ด้านล่างครับ
ตัวอย่าง #
หวังว่าบทความนี้จะช่วยให้หลายๆท่านเห็นภาพการ Reverse Engineering API แบบง่ายๆเพื่อเอาไปใช้ประยุกต์กับงานต่อไปได้นะครับ สำหรับท่านใดมีคำถามสามารถสอบถามเข้ามาที่ Inbox ของ Facebook Page ได้เลยนะครับ